{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 255,
   "metadata": {
    "tags": [
     "remove-cell"
    ]
   },
   "outputs": [],
   "source": [
    "# Reference: https://jupyterbook.org/interactive/hiding.html\n",
    "# Use {hide, remove}-{input, output, cell} tags to hiding content\n",
    "\n",
    "import sys\n",
    "import os\n",
    "if not any(path.endswith('textbook') for path in sys.path):\n",
    "    sys.path.append(os.path.abspath('../../..'))\n",
    "from textbook_utils import *"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Gradient Descent Basics\n",
    "\n",
    "Gradient descent is based on the notion that for many loss functions, the function is roughly linear in small neighborhoods of the parameter. {numref}`Figure %s <gd-diagram>` gives a diagram of the basic idea."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```{figure} figures/gd-diagram.png\n",
    "---\n",
    "name: gd-diagram\n",
    "---\n",
    "\n",
    "The technique of gradient descent moves in small increments toward the minimizing parameter value\n",
    "```"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the diagram, we have drawn the tangent line to the loss curve $ L $ at some point $ {\\theta} $ to the left of the minimizing value, $ \\hat{\\theta} $. Notice that the slope of the tangent line is negative. A short step to the right of $ {\\theta} $ to $ {\\theta}+\\textrm{s} $, for some small amount $ \\textrm{s} $, gives a point on the tangent line close to the loss at $ {\\theta}+\\textrm{s} $, and this loss is smaller than $ L(\\tilde{\\theta}) $. That is, since the slope, $ b $,  is negative, and the tangent line approximates the loss function in a neighborhood of $ {\\theta} $, we have:\n",
    "\n",
    "$$\n",
    "L({\\theta} + \\textrm{s}) \\approx  L(\\theta) + b\\times\\textrm{s} < L(\\theta)\n",
    "$$\n",
    "\n",
    "So, taking a small step to the right of this $ {\\theta} $ decreases the loss. On the other hand, on the other side of $ \\hat\\theta $ in the diagram in  {numref}`Figure %s <gd-diagram>`, the slope is positive, and taking a small step to the left decreases the loss.\n",
    "\n",
    "When we take repeated small steps in the direction indicated by whether the slope of the tangent line is positive or negative at each new step, this leads to smaller and smaller values of the average loss and eventually brings us to the minimizing value $ \\hat{\\theta} $ (or very close to it). This is the basic idea behind gradient descent."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "More formally, to minimize $ L (\\boldsymbol{\\theta}) $ for a general vector of parameters,  $ \\boldsymbol{\\theta} $, the gradient (first-order partial derivative) determines the direction and size of the step to take. If we write the gradient, $ \\nabla_\\theta L(\\boldsymbol{\\theta})  $, as  simply $ g( \\boldsymbol{\\theta})$, then gradient descent says the increment or step is $ -\\alpha g( \\boldsymbol{\\theta}) $ for some small positive $ \\alpha $. Then the average loss at the new position is: \n",
    "\n",
    "$$\n",
    "\\begin{aligned}\n",
    "L(\\theta + (-\\alpha g( \\boldsymbol{\\theta})) & \\approx L(\\theta) - \\alpha g( \\boldsymbol{\\theta})^T g( \\boldsymbol{\\theta}) \\\\\n",
    " & < L(\\theta)\n",
    "\\end{aligned}\n",
    "$$\n",
    "\n",
    "Note that $ g( \\boldsymbol{\\theta})$ is a $p \\times 1 $ vector and $ g( \\boldsymbol{\\theta})^T g( \\boldsymbol{\\theta}) $ is positive."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The steps in the gradient descent algorithm go as follows:\n",
    "\n",
    "1. Choose a starting value, called $ \\boldsymbol{\\theta}^{(0)} $ (a common choice is $ \\boldsymbol{\\theta}^{(0)} = 0 $).\n",
    "2. Compute $ \\boldsymbol{\\theta}^{(t+1)} =  \\boldsymbol{\\theta}^{(t)} - \\alpha g( \\boldsymbol{\\theta}) $.\n",
    "3. Repeat step 2 until $ \\boldsymbol{\\theta}^{(t+1)} $ doesn't change (or changes little) between iterations. \n",
    "\n",
    "The quantity $ \\alpha $ is called the *learning rate*. Setting $ \\alpha $ can be\n",
    "tricky. It needs to be small enough to not overshoot the minimum but large\n",
    "enough to arrive at the minimum in reasonably few steps (see {numref}`Figure %s\n",
    "<gd-learning-rate>`). There are many strategies for setting $ \\alpha $. For\n",
    "example, it can be useful to decrease $ \\alpha $ over time. When $ \\alpha $\n",
    "changes between iterations, we use the notation $ \\alpha^{(t)} $ to indicate\n",
    "that the learning rate varies during the search. "
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```{figure} figures/learning_rate.png\n",
    "---\n",
    "name: gd-learning-rate\n",
    "---\n",
    "\n",
    "A small learning rate requires many steps to converge (left), and a large learning rate can diverge (right); choosing the learning rate well leads to fast convergence on the minimizing value (middle)\n",
    "```"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The gradient descent algorithm is simple yet powerful since we can use it for many types of models and many types of loss functions. It is the computational tool of choice for fitting many models, including linear regression on large datasets and logistic regression. We demonstrate the algorithm to fit a constant to the bus delay data (from {numref}`Chapter %s <ch:modeling>`) next."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.11"
  },
  "toc": {
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": true,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
